<?php

class Silk_Http_Router {

    protected static $instance = null;

    public static function GetInstance(){
	if (self::$instance === null)
	    self::$instance = new Silk_Http_Router();

	return self::$instance;
    }

    protected $_routes = array();

    protected $_auto404           = array(
	'/favicon.ico',
	'/sitemap.xml'
    );

    protected $uri = null;
    protected $request = null;
    protected $response = null;

    private function __construct(){
	include(BASE_DIR . 'config/routes.php');
	$this->_routes = $routes;

    }

    public function getRequest(){
	return $this->request;
    }

    public function &getResponse(){
	return $this->response;
    }

    public function dispatch($pUri){
	$this->request = new Silk_Http_Request($this->uri);
	$this->response = new Silk_Http_Response();
	$this->route($pUri);
    }

    public function goto($pController, $pAction, array $pParameters=array(), Silk_Http_Request $pRequest=null){
	logdebug("Silk_Http_Router.goto: Called {$pController}.{$pAction}");
	return $this->execute($pController, $pAction, $pParameters, $pRequest);
    }

    public function buildUrl($pRouteName, array $pParameters=array()){

	if (array_key_exists($pRouteName, $this->_routes)){

	    $route = $this->_routes[$pRouteName];

	    $route_parts = explode('/', $route['route']);
	    $resolved_route_parts = array();

	    foreach ($route_parts as $i => $rpart){

		if (preg_match('/^:(.*)/', $rpart, $matches)){
		    $var_name = $matches[1];

		    if (array_key_exists($var_name, $pParameters)){

			$resolved_route_parts[$i] = $pParameters[$var_name];

		    } else
			throw new Silk_Exception('Route parameter not defined - ' . $var_name);

		} else
		    $resolved_route_parts[$i] = $rpart;
	    }

	    $url = implode('/', $resolved_route_parts);
	    return $url;

	} else
	    throw new Silk_Exception('No route defined - ' . $pRouteName);
    }

    protected function route($pUri){

	logdebug("Silk_Http_Router.route: Called $pUri");

	if (preg_match('/\?.+/', $pUri))
	    $this->uri = preg_replace('/\?.+$/', '', $pUri);
	else
	    $this->uri = $pUri;

	$parameters = array();

	if (in_array($this->uri, $this->_auto404)){
	    $controller = 'error';
	    $action 	= 'noroute';
	}


	if ($this->uri == '/'){
	    $controller = 'index';
	    $action = 'index';
	} else {

	    $match = false;

	    foreach ($this->_routes as $route_name => $route){

		$uri_parts = explode('/', $this->uri);
		$route_parts = explode('/', $route['route']);

		foreach ($uri_parts as $i => $part){
		    $vars = array();

		    if (array_key_exists($i, $route_parts)){
			if (preg_match('/^:(.*)/', $route_parts[$i], $matches)){
			    $vars[preg_replace('/^:/', '', $matches[1])] = $uri_parts[$i];
			} else if ($part != $route_parts[$i]){
			    $match = false;
			    break;
			}

			$match = true;

		    } else
			$match = false;
		}

		if ($match){
		    $controller = $route['controller'];
		    $action     = $route['action'];
		    $parameters = $vars;
		    break;
		}
	    }

	    if (!$match){
		$orouter = Silk_Object_Router::GetInstance();
		$object_match = $orouter->dispatch($this->uri);
		if ($object_match){
		    $controller = 'object';
		    $action     = 'render';
		    $parameters = array_merge(
			$parameters,
			array(
			    '_object_id' => $orouter->getMatchedObject()->object_id
			)
		    );
		} else {
		    logdebug('No routes match');
		    $controller	= 'error';
		    $action 	= 'noroute';
		}
	    }

	}

	if (count($_REQUEST)){
	    $parameters = array_merge(
		$parameters,
		$_REQUEST
	    );
	}

	return $this->execute($controller, $action, $parameters);


    }

    protected function execute($pController, $pAction, array $pParameters=array()){

	logdebug("Silk_Http_Router.execute: Called {$pController}.{$pAction}");

	$dispatcher = new Silk_Controller_Dispatcher($this);

	try {

	$dispatcher->setRequest($this->request)
	    ->setResponse($this->response)
	    ->setController($pController)
	    ->setAction($pAction)
	    ->setParameters($pParameters)
	    ->dispatch();

	} catch (Exception $x){
 	    die($x->getMessage());
	    $dispatcher->setRequest($this->request)
		->setResponse($this->response)
		->setController('error')
		->setAction('default')
		->setParameters(array('exception' => $x))
		->dispatch();
	}
	return true;
    }


}

?>